TODO
- group by start/end - done -
- plot by large candidate
- include noAnt - done -
- set thresholds for evaluation of candidates
library(ggplot2)
library(data.table)
Load all good candidates for all GAGA species (1st filter: Take only good candidates from both databases)
Load all tsv files for the different GAGA ids in a list of data frames.
#tsv2load<-"/Users/lukas/sciebo/Projects/LGT/results/GAGA.LGT/*/results/*.*.lgt.good.candidates.tsv"
tsv2load<-"/Users/Janina/sciebo/GAGA.LGT/*/results/*.*.lgt.good.candidates.tsv"
dataFiles <- lapply(Sys.glob(tsv2load), read.csv,sep="\t")
add the name
Add the GAGA id as a name to the different list elements.
ids<-gsub(".*/(.+-[0-9]+)\\..+.lgt.good.candidates.tsv","\\1",Sys.glob(tsv2load))
type<-gsub(".*/(.+-[0-9]+)\\.(.+).lgt.good.candidates.tsv","\\2",Sys.glob(tsv2load))
names(dataFiles)<-paste(ids,type,sep=".")
Combine all GAGA ids
Combine tsv files from all GAGA ids to one big data frame.
head(df)
head(df)
extract broad locus start and stop
loci<-read.csv(text=gsub(":","-",df$locus),sep = "-",as.is = T,fill = T,blank.lines.skip = F,header=F)
df$locus.start<-loci$V2
df$locus.end<-loci$V3
df$locus.length<-df$locus.end-df$locus.start
plot overview plots
plot histrograms of different metrics to see how they are distributed.
ggplot(df, aes(x=gc)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

ggplot(df, aes(x=ce)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

ggplot(df, aes(x=ct4)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

ggplot(df, aes(x=ct6)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

ggplot(df, aes(x=locus.length)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

ggplot(df, aes(x=cand.end-cand.start)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

df$log.besteval<- -log(df$besteval,10)
df$log.besteval[df$log.besteval==Inf]<-max(df$log.besteval[df$log.besteval!=Inf])+1
ggplot(df, aes(x=df$log.besteval)) +
geom_histogram(fill="red", alpha=0.5, position="identity")+theme_classic()

Test different filters
What if we filter by evalue (evalue< 1e-50), ct4 (ct4>0.25) and the length of the candidate (length>100)?
dfFilter2<-subset(df,log.besteval>50 & ct4>0.25 & cand.end-cand.start>100 & locus.start > 1000 & locus.length<50000)
What if we remove anything at the beginnign of a scaffold and loci over 50 kb? These are more likely to be misassemblies. (Note that this still leaves in candidates at the very end of scaffolds, which also are more likely misassemblies).
dfFilter2<-subset(df,log.besteval>20 & ct4>0.25 & cand.end-cand.start>100 & locus.start > 1000)
Summarize candidates in close proximity into one larger locus
One row per broad start/stop coordinates
required libraries
library(dplyr)
library(ggrepel)
- Create a data frame that has in each row one larger locus (often containing several lgt candidates).
# keep one row per larger locus and paste together all the info for the different LGT candidates contained in this locus
## https://stackoverflow.com/questions/40033625/concatenating-all-rows-within-a-group-using-dplyr/40033725
dfC <- dplyr::group_by(df, locus) %>%
dplyr::summarise_each(funs(paste(unique(.), collapse = ";")))
# filter this by locus dataframe to only keep loci that have at least one good candidate (i.e. that was contained in the dfFilter2 dataframe)
dfC.filtered<-subset(dfC,locus %in% unique(dfFilter2$locus),select=c(locus,.id,cand.locus,cand.start,cand.end,bestProHit,scaffold,start,end,gc,gcs,ce))
# save data frame to file
write.table(dfC.filtered,"/Users/lukas/sciebo/Projects/LGT/results/GAGA.LGT.filtered.tsv",sep="\t",quote = F,row.names = F)
# for plotting
# split the unfiltered large df dataframe by "locus" into a list of dataframes (one list element per locus)
dfSplit<-split(df,f=paste(df$.id,df$locus,sep="."))
# filter the list of dataframes to only retain those that contain a LGT from the dfFilter2 dataframe
dfSplit.filtered<-dfSplit[unique(paste(dfFilter2$.id,dfFilter2$locus,sep="."))]
Plot each locus
# Define a function containing a ggplot command. This function will be applied to each element of dfSplit.filtered (the list of dataframes)
plotLGTlocus<-function(locusData){
locusData$logeval<- -log(locusData$besteval,10)
locusData$logeval[!is.finite(locusData$logeval)]<- 350
locusData$species<-substr(gsub(".*;","",locusData$bestProHit),1,20)
ggplotLGT<-ggplot(locusData)+
geom_rect(aes(xmin=cand.start,xmax=cand.end,ymin=1,ymax=0,fill=logeval),size=0)+
coord_cartesian(xlim=c(min(locusData$locus.start),max(locusData$locus.end)),ylim=c(0,5))+
geom_text_repel(
aes(x=cand.start,y=1,label=species),
force_pull = 0, # do not pull toward data points
nudge_y = 0.5,
direction = "x",
angle = 90,
hjust = 0,
segment.size = 0.2,
max.iter = 1e4, max.time = 1
)+
scale_fill_gradient(low="steelblue",high = "red",limits = c(20,350),na.value = "grey90")+
ggtitle(locusData$.id[1])+
theme_classic()+
xlab(locusData$locus[1])+
guides(y="none")+
ylab("")+
theme(legend.position="right")
return(ggplotLGT)
}
# test the plotting function
plotLGTlocus(dfSplit.filtered[[1]])
# run plotting function over all elements in the dfSplit.filtered list, i.e. over all loci.
list.of.plots<-lapply(dfSplit.filtered,FUN=plotLGTlocus)
# save all plots (adjust path to your system)
lapply(1:length(list.of.plots), function(i){
ggsave(filename=paste0("/Users/lukas/sciebo/Projects/LGT/results/LGT.filtered/",gsub(":","-",names(list.of.plots)[i]),".pdf"), plot=list.of.plots[[i]])
})
LS0tCnRpdGxlOiAiUiBOb3RlYm9vazogQW5hbHlzZSBMR1QgY2FuZGlkYXRlcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMjIFRPRE8KLSBncm91cCBieSBzdGFydC9lbmQgLSBkb25lIC0KLSBwbG90IGJ5IGxhcmdlIGNhbmRpZGF0ZQotIGluY2x1ZGUgbm9BbnQgLSBkb25lIC0KLSBzZXQgdGhyZXNob2xkcyBmb3IgZXZhbHVhdGlvbiBvZiBjYW5kaWRhdGVzIAoKYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkYXRhLnRhYmxlKQpgYGAKCiMgTG9hZCBhbGwgZ29vZCBjYW5kaWRhdGVzIGZvciBhbGwgR0FHQSBzcGVjaWVzICgxc3QgZmlsdGVyOiBUYWtlIG9ubHkgZ29vZCBjYW5kaWRhdGVzIGZyb20gYm90aCBkYXRhYmFzZXMpCgpMb2FkIGFsbCB0c3YgZmlsZXMgZm9yIHRoZSBkaWZmZXJlbnQgR0FHQSBpZHMgaW4gYSBsaXN0IG9mIGRhdGEgZnJhbWVzLgpgYGB7cn0KI3RzdjJsb2FkPC0iL1VzZXJzL2x1a2FzL3NjaWViby9Qcm9qZWN0cy9MR1QvcmVzdWx0cy9HQUdBLkxHVC8qL3Jlc3VsdHMvKi4qLmxndC5nb29kLmNhbmRpZGF0ZXMudHN2Igp0c3YybG9hZDwtIi9Vc2Vycy9KYW5pbmEvc2NpZWJvL0dBR0EuTEdULyovcmVzdWx0cy8qLioubGd0Lmdvb2QuY2FuZGlkYXRlcy50c3YiCmRhdGFGaWxlcyA8LSBsYXBwbHkoU3lzLmdsb2IodHN2MmxvYWQpLCByZWFkLmNzdixzZXA9Ilx0IikKYGBgCgojIGFkZCB0aGUgbmFtZSAKQWRkIHRoZSBHQUdBIGlkIGFzIGEgbmFtZSB0byB0aGUgZGlmZmVyZW50IGxpc3QgZWxlbWVudHMuCmBgYHtyfQppZHM8LWdzdWIoIi4qLyguKy1bMC05XSspXFwuLisubGd0Lmdvb2QuY2FuZGlkYXRlcy50c3YiLCJcXDEiLFN5cy5nbG9iKHRzdjJsb2FkKSkKdHlwZTwtZ3N1YigiLiovKC4rLVswLTldKylcXC4oLispLmxndC5nb29kLmNhbmRpZGF0ZXMudHN2IiwiXFwyIixTeXMuZ2xvYih0c3YybG9hZCkpCm5hbWVzKGRhdGFGaWxlcyk8LXBhc3RlKGlkcyx0eXBlLHNlcD0iLiIpCmBgYAoKCiMgQ29tYmluZSBhbGwgR0FHQSBpZHMKQ29tYmluZSB0c3YgZmlsZXMgZnJvbSBhbGwgR0FHQSBpZHMgdG8gb25lIGJpZyBkYXRhIGZyYW1lLgpgYGB7cn0KCmRmPC1yYmluZGxpc3QoZGF0YUZpbGVzLGlkY29sID0gVCkKCmBgYAoKCmBgYHtyfQpoZWFkKGRmKQpgYGAKCgojIGV4dHJhY3QgZXZhbApFeHRyYWN0IHRoZSBldmFsdWUgb2YgdGhlIGJlc3QgcHJva2FyeW90aWMgaGl0ICh0YWtlIGV2YWwgZnJvbSBjb2x1bW4gImJlc3RQcm9IaXQiKQpNYWtlIGEgbmV3IGRhdGEgdGFibGUgKGNhbGxlZCAiYmVzdGJsYXN0aGl0cyIpIHdpdGggdGhlIGNvbHVtbiAiYmVzdGV2YWwiICsgImJlc3RQcm9IaXQiIGZyb20gdGhlIGRmCmBgYHtyfQpiZXN0Ymxhc3RoaXRzPC1yZWFkLmNzdih0ZXh0PWFzLmNoYXJhY3RlcihkZiRiZXN0UHJvSGl0KSxzZXAgPSAiOyIsYXMuaXMgPSBULGZpbGwgPSBULGJsYW5rLmxpbmVzLnNraXAgPSBGLGhlYWRlcj1GKQpkZiRiZXN0ZXZhbDwtYmVzdGJsYXN0aGl0cyRWNApgYGAKCiMgZXh0cmFjdCBicm9hZCBsb2N1cyBzdGFydCBhbmQgc3RvcCAKYGBge3J9CmxvY2k8LXJlYWQuY3N2KHRleHQ9Z3N1YigiOiIsIi0iLGRmJGxvY3VzKSxzZXAgPSAiLSIsYXMuaXMgPSBULGZpbGwgPSBULGJsYW5rLmxpbmVzLnNraXAgPSBGLGhlYWRlcj1GKQpkZiRsb2N1cy5zdGFydDwtbG9jaSRWMgpkZiRsb2N1cy5lbmQ8LWxvY2kkVjMKZGYkbG9jdXMubGVuZ3RoPC1kZiRsb2N1cy5lbmQtZGYkbG9jdXMuc3RhcnQKCmBgYAoKIyBwbG90IG92ZXJ2aWV3IHBsb3RzCnBsb3QgaGlzdHJvZ3JhbXMgb2YgZGlmZmVyZW50IG1ldHJpY3MgdG8gc2VlIGhvdyB0aGV5IGFyZSBkaXN0cmlidXRlZC4KYGBge3J9CmdncGxvdChkZiwgYWVzKHg9Z2MpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIiwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSt0aGVtZV9jbGFzc2ljKCkKCmdncGxvdChkZiwgYWVzKHg9Y2UpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIiwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSt0aGVtZV9jbGFzc2ljKCkKCmdncGxvdChkZiwgYWVzKHg9Y3Q0KSkgKwogIGdlb21faGlzdG9ncmFtKGZpbGw9InJlZCIsIGFscGhhPTAuNSwgcG9zaXRpb249ImlkZW50aXR5IikrdGhlbWVfY2xhc3NpYygpCgpnZ3Bsb3QoZGYsIGFlcyh4PWN0NikpICsKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJyZWQiLCBhbHBoYT0wLjUsIHBvc2l0aW9uPSJpZGVudGl0eSIpK3RoZW1lX2NsYXNzaWMoKQoKZ2dwbG90KGRmLCBhZXMoeD1sb2N1cy5sZW5ndGgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIiwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSt0aGVtZV9jbGFzc2ljKCkKCmdncGxvdChkZiwgYWVzKHg9Y2FuZC5lbmQtY2FuZC5zdGFydCkpICsKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJyZWQiLCBhbHBoYT0wLjUsIHBvc2l0aW9uPSJpZGVudGl0eSIpK3RoZW1lX2NsYXNzaWMoKQoKZGYkbG9nLmJlc3RldmFsPC0gLWxvZyhkZiRiZXN0ZXZhbCwxMCkKZGYkbG9nLmJlc3RldmFsW2RmJGxvZy5iZXN0ZXZhbD09SW5mXTwtbWF4KGRmJGxvZy5iZXN0ZXZhbFtkZiRsb2cuYmVzdGV2YWwhPUluZl0pKzEKZ2dwbG90KGRmLCBhZXMoeD1kZiRsb2cuYmVzdGV2YWwpKSArCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIiwgYWxwaGE9MC41LCBwb3NpdGlvbj0iaWRlbnRpdHkiKSt0aGVtZV9jbGFzc2ljKCkKYGBgCgoKIyBUZXN0IGRpZmZlcmVudCBmaWx0ZXJzCgpXaGF0IGlmIHdlIGZpbHRlciBieSBldmFsdWUgKGV2YWx1ZTwgMWUtNTApLCBjdDQgKGN0ND4wLjI1KSBhbmQgdGhlIGxlbmd0aCBvZiB0aGUgY2FuZGlkYXRlIChsZW5ndGg+MTAwKT8KYGBge3J9CmRmRmlsdGVyPC1zdWJzZXQoZGYsbG9nLmJlc3RldmFsPjUwICYgY3Q0PjAuMjUgJiBjYW5kLmVuZC1jYW5kLnN0YXJ0PjEwMCkKYGBgCgpXaGF0IGlmIHdlIHJlbW92ZSBhbnl0aGluZyBhdCB0aGUgYmVnaW5uaWduIG9mIGEgc2NhZmZvbGQgYW5kIGxvY2kgb3ZlciA1MCBrYj8KVGhlc2UgYXJlIG1vcmUgbGlrZWx5IHRvIGJlIG1pc2Fzc2VtYmxpZXMuIChOb3RlIHRoYXQgdGhpcyBzdGlsbCBsZWF2ZXMgaW4gY2FuZGlkYXRlcyBhdCB0aGUgdmVyeSBlbmQgb2Ygc2NhZmZvbGRzLCB3aGljaCBhbHNvIGFyZSBtb3JlIGxpa2VseSBtaXNhc3NlbWJsaWVzKS4KYGBge3J9CmRmRmlsdGVyMjwtc3Vic2V0KGRmLGxvZy5iZXN0ZXZhbD4yMCAmIGN0ND4wLjI1ICYgY2FuZC5lbmQtY2FuZC5zdGFydD4xMDAgJiBsb2N1cy5zdGFydCA+IDEwMDApCmBgYAoKCiMgU3VtbWFyaXplIGNhbmRpZGF0ZXMgaW4gY2xvc2UgcHJveGltaXR5IGludG8gb25lIGxhcmdlciBsb2N1cwpPbmUgcm93IHBlciBicm9hZCBzdGFydC9zdG9wIGNvb3JkaW5hdGVzCgojIyByZXF1aXJlZCBsaWJyYXJpZXMKYGBge3J9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dyZXBlbCkKYGBgCgoxLiBDcmVhdGUgYSBkYXRhIGZyYW1lIHRoYXQgaGFzIGluIGVhY2ggcm93IG9uZSBsYXJnZXIgbG9jdXMgKG9mdGVuIGNvbnRhaW5pbmcgc2V2ZXJhbCBsZ3QgY2FuZGlkYXRlcykuCmBgYHtyfQojIGtlZXAgb25lIHJvdyBwZXIgbGFyZ2VyIGxvY3VzIGFuZCBwYXN0ZSB0b2dldGhlciBhbGwgdGhlIGluZm8gZm9yIHRoZSBkaWZmZXJlbnQgTEdUIGNhbmRpZGF0ZXMgY29udGFpbmVkIGluIHRoaXMgbG9jdXMKCiMjIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQwMDMzNjI1L2NvbmNhdGVuYXRpbmctYWxsLXJvd3Mtd2l0aGluLWEtZ3JvdXAtdXNpbmctZHBseXIvNDAwMzM3MjUKZGZDIDwtIGRwbHlyOjpncm91cF9ieShkZiwgbG9jdXMpICU+JQogICAgICAgIGRwbHlyOjpzdW1tYXJpc2VfZWFjaChmdW5zKHBhc3RlKHVuaXF1ZSguKSwgY29sbGFwc2UgPSAiOyIpKSkKCiMgZmlsdGVyIHRoaXMgYnkgbG9jdXMgZGF0YWZyYW1lIHRvIG9ubHkga2VlcCBsb2NpIHRoYXQgaGF2ZSBhdCBsZWFzdCBvbmUgZ29vZCBjYW5kaWRhdGUgKGkuZS4gdGhhdCB3YXMgY29udGFpbmVkIGluIHRoZSBkZkZpbHRlcjIgZGF0YWZyYW1lKQpkZkMuZmlsdGVyZWQ8LXN1YnNldChkZkMsbG9jdXMgJWluJSB1bmlxdWUoZGZGaWx0ZXIyJGxvY3VzKSxzZWxlY3Q9Yyhsb2N1cywuaWQsY2FuZC5sb2N1cyxjYW5kLnN0YXJ0LGNhbmQuZW5kLGJlc3RQcm9IaXQsc2NhZmZvbGQsc3RhcnQsZW5kLGdjLGdjcyxjZSkpCgojIHNhdmUgZGF0YSBmcmFtZSB0byBmaWxlCndyaXRlLnRhYmxlKGRmQy5maWx0ZXJlZCwiL1VzZXJzL2x1a2FzL3NjaWViby9Qcm9qZWN0cy9MR1QvcmVzdWx0cy9HQUdBLkxHVC5maWx0ZXJlZC50c3YiLHNlcD0iXHQiLHF1b3RlID0gRixyb3cubmFtZXMgPSBGKQoKIyBmb3IgcGxvdHRpbmcKIyBzcGxpdCB0aGUgdW5maWx0ZXJlZCBsYXJnZSBkZiBkYXRhZnJhbWUgYnkgImxvY3VzIiBpbnRvIGEgbGlzdCBvZiBkYXRhZnJhbWVzIChvbmUgbGlzdCBlbGVtZW50IHBlciBsb2N1cykKZGZTcGxpdDwtc3BsaXQoZGYsZj1wYXN0ZShkZiQuaWQsZGYkbG9jdXMsc2VwPSIuIikpCgojIGZpbHRlciB0aGUgbGlzdCBvZiBkYXRhZnJhbWVzIHRvIG9ubHkgcmV0YWluIHRob3NlIHRoYXQgY29udGFpbiBhIExHVCBmcm9tIHRoZSBkZkZpbHRlcjIgZGF0YWZyYW1lCmRmU3BsaXQuZmlsdGVyZWQ8LWRmU3BsaXRbdW5pcXVlKHBhc3RlKGRmRmlsdGVyMiQuaWQsZGZGaWx0ZXIyJGxvY3VzLHNlcD0iLiIpKV0KCgpgYGAKCiMgUGxvdCBlYWNoIGxvY3VzCgpgYGB7cn0KIyBEZWZpbmUgYSBmdW5jdGlvbiBjb250YWluaW5nIGEgZ2dwbG90IGNvbW1hbmQuIFRoaXMgZnVuY3Rpb24gd2lsbCBiZSBhcHBsaWVkIHRvIGVhY2ggZWxlbWVudCBvZiBkZlNwbGl0LmZpbHRlcmVkICh0aGUgbGlzdCBvZiBkYXRhZnJhbWVzKQpwbG90TEdUbG9jdXM8LWZ1bmN0aW9uKGxvY3VzRGF0YSl7CiAgICBsb2N1c0RhdGEkbG9nZXZhbDwtIC1sb2cobG9jdXNEYXRhJGJlc3RldmFsLDEwKQogICAgbG9jdXNEYXRhJGxvZ2V2YWxbIWlzLmZpbml0ZShsb2N1c0RhdGEkbG9nZXZhbCldPC0gMzUwCiAgICBsb2N1c0RhdGEkc3BlY2llczwtc3Vic3RyKGdzdWIoIi4qOyIsIiIsbG9jdXNEYXRhJGJlc3RQcm9IaXQpLDEsMjApCiAgICBnZ3Bsb3RMR1Q8LWdncGxvdChsb2N1c0RhdGEpKwogICAgICAgICAgZ2VvbV9yZWN0KGFlcyh4bWluPWNhbmQuc3RhcnQseG1heD1jYW5kLmVuZCx5bWluPTEseW1heD0wLGZpbGw9bG9nZXZhbCksc2l6ZT0wKSsKICAgICAgICAgIGNvb3JkX2NhcnRlc2lhbih4bGltPWMobWluKGxvY3VzRGF0YSRsb2N1cy5zdGFydCksbWF4KGxvY3VzRGF0YSRsb2N1cy5lbmQpKSx5bGltPWMoMCw1KSkrCiAgICAgICAgICBnZW9tX3RleHRfcmVwZWwoCiAgICAgICAgICAgIGFlcyh4PWNhbmQuc3RhcnQseT0xLGxhYmVsPXNwZWNpZXMpLAogICAgICAgICAgICBmb3JjZV9wdWxsICAgPSAwLCAjIGRvIG5vdCBwdWxsIHRvd2FyZCBkYXRhIHBvaW50cwogICAgICAgICAgICBudWRnZV95ICAgICAgPSAwLjUsCiAgICAgICAgICAgIGRpcmVjdGlvbiAgICA9ICJ4IiwKICAgICAgICAgICAgYW5nbGUgICAgICAgID0gOTAsCiAgICAgICAgICAgIGhqdXN0ICAgICAgICA9IDAsCiAgICAgICAgICAgIHNlZ21lbnQuc2l6ZSA9IDAuMiwKICAgICAgICAgICAgbWF4Lml0ZXIgPSAxZTQsIG1heC50aW1lID0gMQogICAgICAgICAgICApKwogICAgICAgICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3c9InN0ZWVsYmx1ZSIsaGlnaCA9ICJyZWQiLGxpbWl0cyA9IGMoMjAsMzUwKSxuYS52YWx1ZSA9ICJncmV5OTAiKSsKICAgICAgICAgIGdndGl0bGUobG9jdXNEYXRhJC5pZFsxXSkrCiAgICAgICAgICB0aGVtZV9jbGFzc2ljKCkrCiAgICAgICAgICB4bGFiKGxvY3VzRGF0YSRsb2N1c1sxXSkrCiAgICAgICAgICBndWlkZXMoeT0ibm9uZSIpKwogICAgICAgICAgeWxhYigiIikrCiAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb249InJpZ2h0IikKICAgIHJldHVybihnZ3Bsb3RMR1QpCiAgfQpgYGAKCmBgYHtyfQojIHRlc3QgdGhlIHBsb3R0aW5nIGZ1bmN0aW9uCnBsb3RMR1Rsb2N1cyhkZlNwbGl0LmZpbHRlcmVkW1sxXV0pCmBgYAoKCmBgYHtyfQojIHJ1biBwbG90dGluZyBmdW5jdGlvbiBvdmVyIGFsbCBlbGVtZW50cyBpbiB0aGUgZGZTcGxpdC5maWx0ZXJlZCBsaXN0LCBpLmUuIG92ZXIgYWxsIGxvY2kuCmxpc3Qub2YucGxvdHM8LWxhcHBseShkZlNwbGl0LmZpbHRlcmVkLEZVTj1wbG90TEdUbG9jdXMpCgojIHNhdmUgYWxsIHBsb3RzIChhZGp1c3QgcGF0aCB0byB5b3VyIHN5c3RlbSkKbGFwcGx5KDE6bGVuZ3RoKGxpc3Qub2YucGxvdHMpLCBmdW5jdGlvbihpKXsKICAgICAgZ2dzYXZlKGZpbGVuYW1lPXBhc3RlMCgiL1VzZXJzL2x1a2FzL3NjaWViby9Qcm9qZWN0cy9MR1QvcmVzdWx0cy9MR1QuZmlsdGVyZWQvIixnc3ViKCI6IiwiLSIsbmFtZXMobGlzdC5vZi5wbG90cylbaV0pLCIucGRmIiksIHBsb3Q9bGlzdC5vZi5wbG90c1tbaV1dKQogIH0pCmBgYAo=